/* eslint-disable */
class Vue {
  constructor(options) {
    this.$options = options;
    this.$data = options.data;
    this.$el = options.el;

    observe(this.$data);

    proxy(this);

    new Complie(this, this.$el);
  }
}

function observe(obj) {
  if (typeof obj != "object" || obj == null) return obj;
  Object.keys(obj).forEach((key) => {
    defineReactive(obj, key, obj[key]);
  });
}

// 响应式
function defineReactive(obj, key, value) {
  let dep = new Dep();
  Object.defineProperty(obj, key, {
    get() {
      Dep.target && dep.addSub(Dep.target);
      return value;
    },
    set(newVal) {
      if (newVal != value) {
        value = newVal;
        observe(newVal);

        dep.notify();
      }
    },
  });
}

// 代理this.$data
function proxy(vm) {
  Object.keys(vm.$data).forEach((key) => {
    Object.defineProperty(vm, key, {
      get() {
        return vm.$data[key];
      },
      set(newVal) {
        vm.$data[key] = newVal;
      },
    });
  });
}

class Complie {
  constructor(vm, el) {
    let dom = document.querySelector(el);
    this.vm = vm;
    this.complie(dom);
  }
  complie(dom) {
    let childNodes = dom.childNodes;
    if (childNodes) {
      childNodes.forEach((node) => {
        if (this.isElement(node)) {
          let attrs = node.attributes;
          [...attrs].forEach(({ name, value }) => {
            if (this.isDir(name)) {
              let dir = name.slice(2);
              this[dir] && this[dir](node, value);
            }
          });
          if (node.childNodes) {
            this.complie(node);
          }
        } else if (this.isInter(node)) {
          this.complieText(node);
        }
      });
    }
  }
  // 元素节点
  isElement(node) {
    return node.nodeType === 1;
  }
  // 插值
  isInter(node) {
    return node.nodeType === 3 && /\{\{(.*)\}\}/.test(node.textContent);
  }
  isDir(name) {
    return name.startsWith("k-");
  }
  html(node, exp) {
    this.update(node, exp, "html");
  }
  text(node, exp) {
    this.update(node, exp, "text");
  }
  htmlUpdater(node, val) {
    node.innerHTML = val;
  }
  textUpdater(node, val) {
    node.textContent = val;
  }
  update(node, exp, dir) {
    let fn = this[dir + "Updater"];
    fn && fn(node, this.vm[exp]);
    // 为当前组件初始化watcher
    new Watcher(this.vm, exp, (val) => {
      fn && fn(node, val);
    });
  }
  complieText(node) {
    this.update(node, RegExp.$1, "text");
  }
}

class Watcher {
  constructor(vm, key, updater) {
    this.vm = vm;
    this.key = key;
    this.updater = updater;
    Dep.target = this;
    this.vm[key];
    Dep.target = null;
  }
  update() {
    this.updater(this.vm[this.key]);
  }
}
class Dep {
  constructor() {
    this.subs = [];
  }
  addSub(w) {
    this.subs.push(w);
  }
  notify() {
    this.subs.forEach((w) => w.update());
  }
}
